<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link type="text/css" rel="stylesheet" href="/css/scrollbar.css">
  <style>
    * {
      margin: 0;
    }

    html,
    body,
    .editor {
      width: 100%;
      height: 100%;
    }

    .editor-box {
      box-sizing: border-box;
      position: relative;
      height: 100%;
      border: 1px solid #d8dce6;
      border-radius: 4px;
    }

    .placeholder {
      position: absolute;
      color: #bbb;
      font-size: 12px;
      top: 14px;
      left: 13px
    }

    .placeholder.input {
      top: 7px;
      left: 16px
    }

    .editor {
      padding: 12px;
      min-height: 32px;
      font-size: 13px;
      outline: none;
      border-radius: 4px;
      box-sizing: border-box;
      user-select: text;
      -webkit-user-select: text;
      overflow-y: auto;
      color: #606266;
      word-break:break-all;
    }

    .editor img {
      right: -3px;
      position: relative;
      top: 2px;
    }

    .editor.input {
      line-height: 30px;
      padding: 0 15px;
      overflow: hidden;
      white-space: nowrap;
    }

    .two {
      width: calc(100% - 64px);
    }

    .one {
      width: calc(100% - 32px);
    }

    /* .editor:focus {border-color: #3065e8;} */
    .editor:focus::before {
      content: none;
    }

    .context-menu {
      display: inline-block;
      padding: 3px 6px 4px;
      margin-bottom: 1px;
      font-size: 12px;
      line-height: 15px;
      cursor: pointer;
      color: #303133;
      background: #fff;
      border: 1px solid #D8DCE6;
    }

    .context-menu .context-txt {
      display: inline;
    }
  </style>
</head>

<body>
  <div class="editor-box">
    <div class="placeholder"></div>
    <div contentEditable id="editor" class="editor"></div>
  </div>
</body>

</html>
<script>
  const editor = document.getElementById('editor')
  const pla = document.querySelector('.placeholder')
  const editorbox = document.querySelector('.editor-box')

  let composition = false
  let lock = false
  let inputLen = 0
  let maxLen
  pla.addEventListener('click', () => editor.focus())
  editor.addEventListener('paste', paste)
  editor.addEventListener('input', input)
  editor.addEventListener('compositionstart', compositionstart)
  editor.addEventListener('compositionend', compositionend)
  editor.addEventListener('keypress', keypress)
  editor.addEventListener('focus', () => editorbox.style = 'border-color:#3065e8;')
  editor.addEventListener('blur', () => {
    editorbox.style = 'border-color:#d8dce6;'
    setCaret()
    // window.getSelection ? window.getSelection().removeAllRanges() : document.getSelection().empty();
  })
  const setCaret = () => {
    const range = window.getSelection().getRangeAt(0);
    const { startOffset, endOffset } = range;
    if (endOffset - startOffset > 1) {
        const sel = window.getSelection();
        console.log(sel,range);
        range.setStart(range.endContainer.parentElement.childNodes[0], endOffset);
        range.collapse(true);
        sel.removeAllRanges();
        sel.addRange(range);
    }
  }
  // 初始化
  function init(data, placeholder, count, maxlength) {
    maxLen = parseInt(maxlength)

    pla.innerText = placeholder
    pla.className = type === 'text' ? 'placeholder input' : 'placeholder'
    editor.className = type === 'text' ? (count < 2 ? 'editor input one' : 'editor input two') : 'editor'
    editor.innerHTML = data ? data.split('<br>').map(str => `<p>${str?str:'<br>'}</p>`).join('') : '<p><br></p>'
    pla.style.display = data.length ? 'none' : 'block'
  }
  // 组合输入处理 字数限制
  function compositionstart(e) {
    setTimeout(()=>{ composition = true })
  }
  function compositionend(e) {
    setTimeout(()=>{ composition = false })
    if(maxLen) {
      const text = e.data.substring(0, maxLen - inputLen)
      const sel = window.getSelection ? window.getSelection() : document.selection.createRange().text
      const start = sel.baseNode.wholeText.substring(0, sel.baseOffset - e.data.length)
      const end = sel.baseNode.wholeText.substring(sel.baseOffset)
      const range = sel.getRangeAt(0) // 包含当前选区内容的区域对象
      range.deleteContents()
      sel.baseNode.nodeValue = start + (text ? text : end)
      range.setStart(sel.baseNode, (start + text).length)
      range.setEnd(sel.baseNode, (start + text).length)
    }
    setTimeout(()=>{
      input()
    })
  }
  // 输入的字数
  function changeText (nodes) {
    inputLen = 0
    nodes.forEach(item => {
      if(typeof item !== 'string') inputLen += 1
      else if(item !== '<br>') inputLen += item.length
    })
    if(maxLen) lock = inputLen >= maxLen
  }
  // 输入监听
  function input(e) {
    console.log('input',e,composition);
    if(composition) return 
    const nodes = []
    const childNodes = e?.target?.childNodes || editor.childNodes
    if (!childNodes.length) editor.innerHTML = '<p><br></p>'
    pla.style.display = childNodes.length ? 'none' : 'block'
    childNodes.forEach((p, k) => {
      if (p.nodeType === 3) nodes.push(p.data)
      p.childNodes.forEach(node => {
        if (node.nodeType === 3) nodes.push(node.data)
        if (node.nodeType === 1) {
          if (node.nodeName === 'INPUT') nodes.push({ id: node.dataset.id.trim(), type: node.dataset.type.trim(), name: node.value.trim() })
        }
      })
      if (type !== 'text' && p.childNodes.length && childNodes.length !== k + 1) nodes.push('<br>')
    })
    changeText(nodes)
    pla.style.display = nodes.length ? 'none' : 'block'
    window.parent.postMessage({ key, nodes })
  }

  // 禁止回车
  function keypress(e) {
    if (lock) e.preventDefault()
    if (e.keyCode !== 13) return
    e.preventDefault()
    if (type === 'textarea') insertTxt('<br>')
  }
  // 粘贴
  function paste(e) {
    e.preventDefault()
    e.stopPropagation()
    if (!e.target.contentEditable) return
    const cpTxt = e.clipboardData.getData('text')
    cpTxt && insertTxt(cpTxt, true)
  }
  // 插入数据
  function insertTxt(txt) {
    if (lock) return
    document.getElementById('editor').focus()

    const sel = window.getSelection()
    if (sel.getRangeAt && sel.rangeCount) {
      let range = sel.getRangeAt(0) // 包含当前选区内容的区域对象
      range.deleteContents() // Range.deleteContents(): 移除来自 Document 的 Range 内容
      const el = document.createElement('div') // 生成一个 div
      let oldp = range.startContainer.nodeType === 3 ? range.startContainer.parentNode : range.startContainer
      oldp = oldp.nodeName === 'DIV' ? range.startContainer : oldp
      if (txt === '<br>') {
        if (range.startContainer.nodeType === 3) {
          let num = range.startOffset
          Array.prototype.some.call(oldp.childNodes, node => {
            if (node.isSameNode(range.startContainer)) return true
            num += (node.data || node.outerHTML + '').length
          })
          const startStr = (oldp.innerHTML || oldp.data).substring(0, num)
          const endStr = (oldp.innerHTML || oldp.data).substring(num)
          el.innerHTML = `<p>${startStr ? startStr : '<br>'}</p><p>${endStr ? endStr : '<br>'}</p>`
        } else {
          let str = range.startOffset ? '<p>' : '<p><br></p><p>'
          oldp.childNodes.forEach((node, k) => {
            console.log((node.data || node.outerHTML || ''), 'node.data||node.outerHTML');
            str += (node.data || node.outerHTML || '') + ''
            if (k + 1 === range.startOffset) str += '</p><p>'
          })
          if (str.substr(str.length - 3) === '<p>') str += '<br>'
          el.innerHTML = str + '</p>'
        }
      } else {
        el.innerHTML = txt // div 里的内容为上面定义的要添加的元素
      }
      const frag = document.createDocumentFragment()
      let node, lastNode
      while ((node = el.firstChild)) {
        // 把生成 div 里的内容，即 html，移到 frag 里
        lastNode = frag.appendChild(node)
      }
      console.log(frag, oldp);
      if (txt === '<br>') editor.replaceChild(frag, oldp)
      else range.insertNode(frag)
      if (lastNode) {
        range = range.cloneRange()
        if (txt === '<br>') {
          range.setStartBefore(lastNode.childNodes[0])
        } else {
          range.setStartAfter(lastNode)
        }
        range.collapse(true)
        sel.removeAllRanges()
        sel.addRange(range)
      }
      input()
    }
  }
</script>
<script>
  window.onload = function () {
    const head = document.getElementsByTagName('head')
    const link = document.createElement('link')
    link.type = 'text/css'
    link.rel = 'stylesheet'
    link.href = navigator.platform.indexOf('Mac') > -1 ? '/css/mac.css' : '/css/win.css'
    head[0].appendChild(link)
  }
</script>